<?php

if (!function_exists('pricecalc')) {
    function pricecalc($n1, $mode, $n2, $scale = '2'): string {
        switch($mode) {
            case '+':
                return bcadd($n1, $n2, $scale);
            case '-':
                return bcsub($n1, $n2, $scale);
            case '*':
                return bcmul($n1, $n2, $scale);
            case '/':
                return bcdiv($n1, $n2, $scale);
            default:
                throw new Exception('undeined mode');
        }
    }
}

if (!function_exists('export_csv')) {
    /**
     * 导出csv
     *
     * @param String $sFileName
     * @param Array $aTitle
     * @param [type] $oQuery
     * @param Closure $closure
     * @return void
     * @date 2023-05-29
     * 
     * eg: 
     * // 定义表头
     *   $aTitle=[
     *       '管理号', '姓名', '身份证', '性别', '证书类别', '资格名称', '专业名称', '工作单位', '批复文号', '评级方式', '签发单位', '状态', '年代', '取得资格时间', '重试次数', '失败原因' 
     *   ];
     *   // 导出文件名
     *   $csvFileName = "ZC_{$glh_prefix}xxxx.csv";
     *   // 数据源 使用DB::table方式更高效
     *   $oQuery = Zhicheng::query()
     *       ->select('glh','xm','sfz','xb','jb','zc','zy','dw','pfwjh','pjfs','qfdw','zszt','nd','rq','retry','reason')
     *       ->where('glh', 'like', "$glh_prefix%")
     *       ;
     *   // 数据加工
     *   $this->csv($csvFileName,$aTitle,$oQuery,function ($obj){
     *       return [
     *           $obj->glh,$obj->xm,"'$obj->sfz",$obj->xb,$obj->jb,$obj->zc,$obj->zy,$obj->dw,$obj->pfwjh,$obj->pjfs,$obj->qfdw,$obj->zszt,$obj->nd,$obj->rq,$obj->retry,$obj->reason
     *       ];
     *   });
     */
    function export_csv(String $sFileName,Array $aTitle, $oQuery, Closure $closure) {
        // 设置过期时间
        set_time_limit(0);
        //处理需要导出的数据
        //设置好告诉浏览器要下载excel文件的headers
        header('Content-Description: File Transfer');
        header('Content-Type: application/vnd.ms-excel');
        header('Content-Disposition: attachment; filename="'. $sFileName .'"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        $fp = fopen('php://output', 'a');//打开output流
        fwrite($fp, "\xEF\xBB\xBF"); // 写入bom 头 可识别 utf8
        if (!empty($aTitle)) {
            fputcsv($fp, $aTitle);//将数据格式化为CSV格式并写入到output流中
        }
        $accessNum = $oQuery->count();//从数据库获取总量，假设是一百万
        $perSize = 10000;//每次查询的条数
        $pages   = ceil($accessNum / $perSize);
        for($i = 1; $i <= $pages; $i++) {
            //需要导出的数据
            $oCollection = $oQuery->forPage($i,$perSize)->get();
            foreach($oCollection as $obj) {
                $rowData = $closure($obj); //返回 array
                fputcsv($fp, $rowData);
            }
            unset($oCollection);//释放变量的内存
            //刷新输出缓冲到浏览器
            if (ob_get_level() > 0) {
                ob_flush();
            }
        }
        fclose($fp);
        exit();
    }
}

if (!function_exists('response_camel')) {
    function response_camel(array &$data) {
        foreach($data as $k => &$v) {
            if ($v instanceof \Illuminate\Support\Collection) {
                $v = $v->toArray();
            }
            if (is_array($v)) {
                response_camel($v);
            }
            $k1 = \Str::camel($k);
            $data[$k1] = $v;
            if ($k != $k1) { unset($data[$k]); }
        }
    }
}

if (!function_exists('show_response')) {
    function show_response($data = null, ?string $msg = '请求成功~', int $code = 200): \Illuminate\Http\Response {
        $data = $data ?? [];
        if (!is_array($data)) $data = ['data'=>$data];
        $data = ['code'=>$code, 'msg'=>$msg ?? '后台异常~', ...$data];
        response_camel($data);
        return response($data);
        
        $result = ['code'=>$code, 'msg'=>$msg ?? '后台异常~'];
        if (!$data) return response($result);
        if (is_array($data)) {
            $result = array_merge($result, $data);
        } else if ($data instanceof \Illuminate\Http\Resources\Json\JsonResource) {
            dd([$data->response()]);
            return $data->additional($result)->response();
            $result = $data->additional($result);
            dd($result);
        } else {
            $result[] = $data;
        }
        return response($result);
    }
}

if (!function_exists('atn')) {
    /** 批量給表字段添加表名前缀, 建议只在model中调用 */
    function atn(array|string $column, $table=null): array|string {
        if (!$table) {
            $called_class = \Illuminate\Support\Str::replace('/','\\','App'.explode('.', explode('app', debug_backtrace()[0]['file'])[1])[0]);
            $table = $called_class::Table;     // baseModel约定对象
        }
        return is_string($column) ? $table.'.'.$column : array_map(fn($it)=>$table.'.'.$it, $column);
    }
}
if (!function_exists('get_value')) {
    // 获取array的值, 兼容驼峰/蛇形key
    function get_value(array $data, string $key, $strict=false): mixed {
        try {
            return $data[$key] ?? $data[\Illuminate\Support\Str::camel($key)] ?? $data[\Illuminate\Support\Str::snake($key)];
        } catch (\Throwable $th) {
            $strict && throw $th;
        }
        return null;
    }
}

if (!function_exists('throw_str')) {
    function throw_str(mixed $result, ?Closure $exCallback=null): mixed {
        return \App\Exceptions\ApiException::throw_str($result, $exCallback);
    }
}

if (!function_exists("redis")) {
    /**
     * Get / set the specified redis value.
     *
     * If an array is passed, we'll assume you want to put to the redis.
     * 
     * suggested use phpredis driver
     *
     * @param  dynamic  null|key|key,default|key,(del|keys)|data|data,expiration
     * @return mixed|bool|int|array|\ruoyi\admin\facades\Redis|\Illuminate\Redis\Connections\PhpRedisConnection
     *
     * @throws \InvalidArgumentException
     */
    function redis() {
        $redis = app('redis');
        $arguments = func_get_args();
        if (empty($arguments)) {
            return $redis;
        }

        $args = $arguments[0];              // 参数
        $acts = $arguments[1] ?? null;      // 行为, null / default_value / _tel_tag / ttl_value
        $TAGS = ['del', 'keys'];

        if (is_string($args)) {
            if ($acts) {
                if (in_array($acts, $TAGS)) {
                    switch($acts) {
                        case 'del':
                            $prefix = config('database.redis.options.prefix');
                            return $redis->del(preg_replace("/^$prefix/", '', $args));
                        case 'keys':
                            return $redis->keys($args);
                    }
                }
                return $redis->get($args) ?? $acts;
            }
            return $redis->get($args);
        }

        // redis([k => v], (expire))
        if (!is_array($args)) {
            throw new InvalidArgumentException(
                'When setting a value in the redis, you must pass an array of key / value pairs.'
            );
        }

        $k = key($args);
        $v = reset($args);
        $ttl = !$acts ? null : ($acts instanceof \Carbon\Carbon ? $acts->diffInSeconds() : $acts); // 允许 Carbon / now() / int(senconds)
        if (!is_string($v)) {
            throw new InvalidArgumentException('redis([k => value]), value not string!');
        }
        return $ttl ? $redis->setex($k, $ttl, $v) : $redis->set($k, $v);
    }
}

if (!function_exists('user')) {
    /**
     * 不区分用户和游客(不走中间件auth:sanctum)也能获取到token对应的user
     */
    function user(string $gurad='sanctum'): ?\App\Models\System\SysUserModel {
        return \Illuminate\Support\Facades\Auth::user() ?? auth($gurad)->user() ?? null;
    }
}
